package com.github.aloxc.plugin.restplus.action;

import com.github.aloxc.plugin.DonotGuard;
import com.github.aloxc.plugin.restplus.common.*;
import com.github.aloxc.plugin.restplus.component.ServiceNode;
import com.github.aloxc.plugin.restplus.component.SettingForm;
import com.github.aloxc.plugin.restplus.entity.*;
import com.github.aloxc.plugin.restplus.i18n.I18Bundle;
import com.github.aloxc.plugin.restplus.listener.IRightClickHandler;
import com.github.aloxc.plugin.restplus.listener.RefreshListener;
import com.github.aloxc.plugin.restplus.listener.SettingListener;
import com.github.aloxc.plugin.restplus.navigation.action.RestServiceItem;
import com.github.aloxc.plugin.restplus.navigator.RestServiceDetail;
import com.github.aloxc.plugin.restplus.navigator.RestServicesNavigator;
import com.github.aloxc.plugin.restplus.popup.PopupBalloon;
import com.github.aloxc.plugin.restplus.resolver.*;
import com.github.aloxc.plugin.restplus.utils.StringUtils;
import com.github.aloxc.plugin.restplus.utils.ToolkitUtil;
import com.intellij.openapi.actionSystem.AnActionEvent;
import com.intellij.openapi.actionSystem.CommonDataKeys;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.wm.ex.ToolWindowEx;
import com.intellij.openapi.wm.ex.ToolWindowManagerEx;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.util.OpenSourceUtil;
import org.jetbrains.annotations.SystemIndependent;
import org.jetbrains.kotlin.asJava.LightClassUtilsKt;
import org.jetbrains.kotlin.psi.KtClass;
import org.jetbrains.kotlin.psi.KtNamedFunction;

import javax.swing.*;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.util.*;
import java.util.List;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

import static com.github.aloxc.plugin.restplus.common.Consts.DOT;
import static com.github.aloxc.plugin.restplus.navigator.RestServicesNavigator.TOOL_WINDOW_ID;
import static com.intellij.openapi.actionSystem.CommonDataKeys.PSI_ELEMENT;
import static com.intellij.openapi.actionSystem.CommonDataKeys.PSI_FILE;

/**
 * 文件中或者点击文件名右键菜单推送到yapi
 * todo: 没考虑RequestMapping 多个值的情况
 *
 * @author leero
 */
@DonotGuard
public class PushYapiAction extends AbstractBaseAction implements IRightClickHandler {
    Editor myEditor;
    private static ExecutorCompletionService<String> executorCompletionService = new ExecutorCompletionService(Executors.newFixedThreadPool(3));

    @Override
    public void actionPerformed(AnActionEvent e) {
        Project project = e.getProject();
        myEditor = e.getData(CommonDataKeys.EDITOR);
        PsiElement psiElement = e.getData(PSI_ELEMENT);
        PsiFile psiFile = (PsiFile) e.getData(PSI_FILE);
        @SystemIndependent final String projectPath = project.getBasePath();
        runIt(project,psiElement,psiFile.getVirtualFile());
//
//        RestServiceItem curItem = allItemList.stream().filter(item -> {
//                if (
//                        item.getPsiMethod().getName().equals(restServiceItem.getPsiMethod().getName())
//                                && item.getPsiClass().getName().equals(restServiceItem.getPsiClass().getName())
//                                && item.getModule().getName().equals(restServiceItem.getModule().getName())
//                                && item.getUrl().equals(restServiceItem.getUrl())
//                                && item.getMethod().name().equals(restServiceItem.getMethod().name())
//                                && item.isFoundRequestBody() == restServiceItem.isFoundRequestBody()
//                ) {
//                    return true;
//                }
//                return false;
//            }).findFirst().orElse(null);
//
//            YapiDTO yapiDTO = yapiResolver.buildYapi(serviceNode);
//
////            ServiceNode serviceNode = (ServiceNode) selNode;
////            YapiDTO yapiDTO = YapiFactory.build(serviceNode);
//            dtoList.add(yapiDTO);
//        PopupBalloon.showPopupBalloon(myEditor, "success ,url:  " + psiClass.getName() + ", 数量" + psiMethods.length, false);
    }

    public void runIt(Project project, PsiElement psiElement, VirtualFile virtualFile){
        PsiClass psiClass = null;
        PsiMethod[] psiMethods = null;

        if (psiElement instanceof PsiMethod) {
            psiMethods = new PsiMethod[]{(PsiMethod) psiElement};
            psiClass = (PsiClass) psiMethods[0].getParent();
        } else if (psiElement instanceof PsiClass) {
            psiClass = (PsiClass) psiElement;
            psiMethods = psiClass.getMethods();
        } else if (psiElement instanceof KtClass) {
            psiClass = LightClassUtilsKt.toLightClass((KtClass) psiElement);
            psiMethods = psiClass.getMethods();
        } else if (psiElement instanceof KtNamedFunction) {
            psiMethods = new PsiMethod[]{LightClassUtilsKt.toLightMethods((KtNamedFunction) psiElement).get(0)};
            psiClass = (PsiClass) psiMethods[0].getParent();
        }
//        OpenSourceUtil.navigate(psiClass);
//        Editor data = new DataContext().getData(CommonDataKeys.EDITOR);
        Module[] modules = ModuleManager.getInstance(project).getModules();
        List<Module> curModuleList = Arrays.stream(modules).filter(module -> {
            String modulePath = module.getModuleFile().getParent().getPath();
            return virtualFile.getPath().contains(modulePath);
        }).sorted((m1, m2) ->
                m2.getModuleFile().getParent().getPath().length() - m1.getModuleFile().getParent().getPath().length()
        ).limit(1).collect(Collectors.toList());
        Module module = curModuleList.get(0);
        if (module == null) {
            //这是从swagger导入的，不支持推送到yapi
            showPopupBalloon(project, I18Bundle.getMessage("com.github.aloxc.plugin.restplus.yapi.swagger.text"), false);
            return;
        }

        RestConfig restConfig = RestConfig.loadConfig(project);
        Yapi yapiConfig = restConfig.getYapi(module);
        if(yapiConfig == null
                || yapiConfig.getProjectId() == null || yapiConfig.getProjectId().length() == 0
                || yapiConfig.getToken() == null || yapiConfig.getToken().length() == 0
                || yapiConfig.getUrl() == null || yapiConfig.getUrl().length() == 0
        ){
            //未配置yapi，弹出配置
            SettingForm settingForm = new SettingForm(project);
            SettingListenerContext context = new SettingListenerContext();
            context.setSettingFormType(SettingFormType.YAPI);
            context.setContext(module.getName());
            SettingListener settingListener = new SettingListener(settingForm, context);
            settingListener.actionPerformed(null);
//            settingForm.getYapiButton().
            return;
        }
        RestConfig config = RestConfig.loadConfig(project);
        if (config == null) {
            showPopupBalloon(project, I18Bundle.getMessage("com.github.aloxc.plugin.restplus.yapi.noconfig.text"), false);
            return;
        }
        Yapi yapi = config.getYapi(module);
        if (yapi == null || StringUtils.isNullOrEmpty(yapi.getToken()) || StringUtils.isNullOrEmpty(yapi.getProjectId()) || StringUtils.isNullOrEmpty(yapi.getUrl())) {
            showPopupBalloon(project, I18Bundle.getMessage("com.github.aloxc.plugin.restplus.yapi.noconfig.text"), false);
            return;
        }

        RestServicesNavigator servicesNavigator = project.getComponent(RestServicesNavigator.class);
        JTree urlTree = servicesNavigator.getUrlTree();
        int rootCount = ((DefaultMutableTreeNode) urlTree.getModel().getRoot()).getChildCount();
        if (rootCount == 0) {
            //还没有初始化
            final ToolWindowManagerEx manager = ToolWindowManagerEx.getInstanceEx(project);
            ToolWindowEx myToolWindow = (ToolWindowEx) manager.getToolWindow(TOOL_WINDOW_ID);
            if (myToolWindow == null) {
                return;
            }
            myToolWindow.show(() -> {
            });
            new RefreshListener(project).doRefreshProject();
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            showPopupBalloon(project, I18Bundle.getMessage("com.github.aloxc.plugin.restplus.EditorPopupMenu.run.usercase.noinit.text"), false);
            return;
        }

        RestServiceDetail restServiceDetail = servicesNavigator.getRestServiceStructure().getRestServiceDetail();
        List<YapiDTO> dtoList = new ArrayList<>();
        IYapiResolver yapiResolver = getYapiResolver(project, psiClass);

        //TODO: 需完善 jaxrs 支持
        String servicePath;


        List<RestServiceItem> allItemList = new ArrayList<>();
        SpringResolver springResolver = new SpringResolver(module);
        JaxrsResolver jaxrsResolver = new JaxrsResolver(module);
        MicronautResolver micronautResolver = new MicronautResolver(module);
        ServiceResolver[] resolvers = {springResolver, jaxrsResolver, micronautResolver};
        for (ServiceResolver resolver : resolvers) {
            List<RestServiceItem> allSupportedServiceItemsInModule = resolver.findAllSupportedServiceItemsInModule();
            allItemList.addAll(allSupportedServiceItemsInModule);
        }
        if (allItemList.size() == 0) {
            return;
        }
        allItemList.get(0).getUrl();

        Map<String, List<RestServiceItem>> classItemMap = allItemList.stream().collect(Collectors.groupingBy(v -> v.getPsiClass().getText()));
        List<RestServiceItem> itemList = classItemMap.get(psiClass.getText());
        List<String> methodNames = Arrays.stream(psiMethods).map(PsiMethod::getName).collect(Collectors.toList());
        List<ServiceNode> serviceList = itemList.stream().filter(sn -> methodNames.contains(sn.getPsiMethod().getName())).map(v -> new ServiceNode(v, false)).collect(Collectors.toList());

        final List<YapiDTO> pushList = new ArrayList<>();
        for (ServiceNode serviceNode : serviceList) {
            YapiDTO yapiDTO = yapiResolver.buildYapi(serviceNode);
            pushList.add(yapiDTO);
        }

        ToolkitUtil.invokeLater(project, new Runnable() {
            @Override
            public void run() {
                try {
                    String url = null;
                    for (final YapiDTO yapiDTO : pushList) {
                        final YApiSaveParam yapiSaveParam = new YApiSaveParam(yapi.getToken(), yapiDTO.getTitle(), yapiDTO.getPath(), yapiDTO.getParams(), yapiDTO.getRequestBody(),
                                yapiDTO.getResponse(), Integer.valueOf(yapi.getProjectId()), yapi.getUrl(), true, yapiDTO.getMethod(), yapiDTO.getDesc(), yapiDTO.getHeader());
                        yapiSaveParam.setReq_body_form(yapiDTO.getReq_body_form());
                        yapiSaveParam.setReq_body_type(yapiDTO.getReq_body_type());
                        yapiSaveParam.setReq_params(yapiDTO.getReq_params());
                        yapiSaveParam.setMenu(yapiDTO.getMenu());
                        YapiResponse yapiResponse = new UploadYapi().uploadSave(yapiSaveParam, pushList.size() > 1);
                        if (yapiResponse.getErrcode() != 0) {
                            showPopupBalloon(project, "sorry ,upload api " + yapiDTO.getPath() + " error cause:" + yapiResponse.getErrmsg(), true);
                        } else {
                            url = (String) yapiResponse.getData();
                        }
                    }
                    StringBuffer html = new StringBuffer();
                    html.append("URL<br/><a href=\"").append(url).append("\">").append(url).append("</a>");
                    String desc = pushList.get(0).getMenu();
                    if(pushList.size() == 1){
                        desc = pushList.get(0).getTitle();
                    }
                    showPopupBalloon(project, "success , you can see["+ desc +"] :  " + html.toString(), false);
                    Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
                    String path = "[" + desc + "]\r\n" + url;
                    clipboard.setContents(new StringSelection(path), null);
                } catch (Exception e1) {
                    showPopupBalloon(project, "可能是工程错误、token错误、yapi地址错误，请检查！", true);

                }
            }
        });
    }
    /**
     * 弹出提示框
     *
     * @param info
     */
    private void showPopupBalloon(Project project, String info, boolean error) {
//        if(myEditor == null){
        //这是从文件列表发起的操作
        PopupBalloon.showNotification(project, info, false);
//        }else{
//            PopupBalloon.showPopupBalloon(myEditor, info, error);
//        }
    }


    /**
     * 通过策略获取yapi业务处理类
     *
     * @param project
     * @param psiMethod
     * @return
     */
    public IYapiResolver getYapiResolver(Project project, PsiMethod psiMethod) {
        Consts.ServiceType serviceType = Consts.ServiceType.getServiceType(psiMethod);
        if (serviceType == Consts.ServiceType.JAXRS) {
            return new JaxrsResolver(project);
        } else if (serviceType == Consts.ServiceType.MICRONAUT) {
            return new MicronautResolver(project);
        } else {
            return new SpringResolver(project);
        }
    }

    /**
     * 通过策略获取yapi业务处理类
     *
     * @param project
     * @param psiClass
     * @return
     */
    public IYapiResolver getYapiResolver(Project project, PsiClass psiClass) {
        Consts.ServiceType serviceType = Consts.ServiceType.getServiceType(psiClass);
        if (serviceType == Consts.ServiceType.JAXRS) {
            return new JaxrsResolver(project);
        } else if (serviceType == Consts.ServiceType.MICRONAUT) {
            return new MicronautResolver(project);
        } else {
            return new SpringResolver(project);
        }
    }

    private void expandAll(JTree tree, TreePath parent, boolean expand) {
        TreeNode node = (TreeNode) parent.getLastPathComponent();
        if (node.getChildCount() > 0) {
            for (Enumeration e = node.children(); e.hasMoreElements(); ) {
                TreeNode n = (TreeNode) e.nextElement();
                TreePath path = parent.pathByAddingChild(n);
                expandAll(tree, path, expand);

            }
        }
        if (expand) {
            tree.expandPath(parent);
        } else {
            tree.collapsePath(parent);
        }
    }

    private TreePath find(DefaultMutableTreeNode root, String s) {
        @SuppressWarnings("unchecked")
        Enumeration<TreeNode> e = root.depthFirstEnumeration();
        while (e.hasMoreElements()) {
            DefaultMutableTreeNode node = (DefaultMutableTreeNode) e.nextElement();
            if (node.toString().equalsIgnoreCase(s)) {
                return new TreePath(node.getPath());
            }
        }
        return null;
    }


    /**
     * spring rest 方法被选中才触发
     *
     * @param e
     */
    @Override
    public void update(AnActionEvent e) {
        PsiElement psiElement = e.getData(CommonDataKeys.PSI_ELEMENT);
        Project project = e.getProject();
        boolean visible = false;
        if (psiElement instanceof PsiClass) {
            //选中的class
            PsiClass psiClass = (PsiClass) psiElement;
            visible = (PsiClassHelper.isRestController(psiClass));
        }
        if (psiElement instanceof PsiMethod) {
            PsiMethod psiMethod = (PsiMethod) psiElement;
            // rest method 或标注了RestController 注解
            visible = (PsiClassHelper.isRestController(psiMethod.getContainingClass()) && PsiMethodHelper.isRestfulMethod(psiMethod));
        }
        if (psiElement instanceof KtClass) {//判断是否是kotlin类
            KtClass ktClass = (KtClass) psiElement;
            PsiClass psiClass = LightClassUtilsKt.toLightClass(ktClass);
            visible = (PsiClassHelper.isRestController(psiClass));
        }
        if (psiElement instanceof KtNamedFunction) {//判断是否是kotlin方法
            KtNamedFunction fun = (KtNamedFunction) psiElement;
            List<PsiMethod> psiMethods = LightClassUtilsKt.toLightMethods(psiElement);
            for (PsiMethod psiMethod : psiMethods) {
                visible = (PsiClassHelper.isRestController(psiMethod.getContainingClass()) && PsiMethodHelper.isRestfulMethod(psiMethod));
            }
        }

        //判断是否设置了yapi，没有设置的话弹出设置界面
        Module module = null;
        try {
            module = getCurrentModule(e);
        } catch (Exception exe) {
            visible = false;
        }

        if (visible) {
            RestConfig restConfig = RestConfig.loadConfig(project);
            Yapi yapi = restConfig.getYapi(module);
            if (yapi == null || StringUtils.isNullOrEmpty(yapi.getToken()) || StringUtils.isNullOrEmpty(yapi.getProjectId()) || StringUtils.isNullOrEmpty(yapi.getUrl())) {
                e.getPresentation().setText(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.request.pushyapi.noconfig.text"));
                e.getPresentation().setDescription(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.request.pushyapi.noconfig.text"));
            } else {
                e.getPresentation().setText(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.request.pushyapi.text"));
                e.getPresentation().setDescription(I18Bundle.getMessage("com.github.aloxc.plugin.restplus.request.pushyapi.text"));
            }
            e.getPresentation().setIcon(Consts.Icons.PUSH_YAPI);
        }
        setActionPresentationVisible(e, visible);
    }

    public static void main(String[] args) {
        List<String> list = new ArrayList();
        long lCount2 = list.stream().filter(ex -> true).count();
        list.add("abcd");
        list.add("ab");
        System.out.println(list.stream().sorted(((o1, o2) -> o2.length() - o1.length())).collect(Collectors.joining(",")));
    }


}
